<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>药品在线招投标系统 - 数据流图</title>
    <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
    <style>
        body {
            font-family: 'Microsoft YaHei', 'SimSun', sans-serif;
            margin: 0;
            padding: 20px;
            background: #f5f5f5;
        }
        .container {
            max-width: 1600px;
            margin: 0 auto;
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            text-align: center;
            color: #2563eb;
            margin-bottom: 10px;
        }
        h2 {
            text-align: center;
            color: #64748b;
            font-size: 18px;
            margin-bottom: 30px;
            font-weight: normal;
        }
        .export-buttons {
            text-align: center;
            margin-bottom: 20px;
        }
        .export-btn {
            background: #2563eb;
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 16px;
            margin: 0 10px;
            transition: background 0.3s;
        }
        .export-btn:hover {
            background: #1e40af;
        }
        .export-btn:disabled {
            background: #94a3b8;
            cursor: not-allowed;
        }
        .status-message {
            text-align: center;
            margin: 10px 0;
            padding: 10px;
            border-radius: 6px;
            display: none;
        }
        .status-message.success {
            background: #d1fae5;
            color: #065f46;
            border: 1px solid #10b981;
        }
        .status-message.error {
            background: #fee2e2;
            color: #991b1b;
            border: 1px solid #ef4444;
        }
        .status-message.info {
            background: #dbeafe;
            color: #1e40af;
            border: 1px solid #3b82f6;
        }
        .diagram-container {
            background: white;
            padding: 20px;
            border: 2px solid #e2e8f0;
            border-radius: 8px;
            margin-bottom: 30px;
            overflow-x: auto;
        }
        .diagram-title {
            text-align: center;
            font-size: 20px;
            font-weight: 600;
            color: #1e293b;
            margin-bottom: 20px;
            padding-bottom: 10px;
            border-bottom: 2px solid #e2e8f0;
        }
        .instructions {
            margin-top: 30px;
            padding: 20px;
            background: #f1f5f9;
            border-radius: 8px;
            border-left: 4px solid #2563eb;
        }
        .instructions h2 {
            color: #1e293b;
            margin-top: 0;
            text-align: left;
            font-size: 20px;
        }
        .instructions h3 {
            color: #1e293b;
            margin-top: 20px;
        }
        .instructions ul {
            color: #64748b;
            line-height: 1.8;
        }
        .instructions table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }
        .instructions table th,
        .instructions table td {
            padding: 10px;
            text-align: left;
            border: 1px solid #cbd5e1;
        }
        .instructions table th {
            background: #e2e8f0;
            font-weight: 600;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>药品在线招投标系统 - 数据流图（DFD）</h1>
        <h2>Data Flow Diagram</h2>
        
        <div class="export-buttons">
            <button class="export-btn" id="exportPngBtn" onclick="exportAsPNG()">📥 导出为PNG图片</button>
            <button class="export-btn" id="exportSvgBtn" onclick="exportAsSVG()">📥 导出为SVG图片</button>
            <button class="export-btn" onclick="exportAsPNGManual()">📥 备用：导出PNG</button>
        </div>
        <div class="status-message" id="statusMessage"></div>
        
        <!-- 0层：上下文图 -->
        <div class="diagram-container">
            <div class="diagram-title">0层：上下文图（Context Diagram）</div>
            <div class="mermaid" id="dfd-level0">
flowchart LR
    BIDDER[投标单位<br/>BIDDER]
    TENDERER[招标单位<br/>TENDERER]
    ADMIN[系统管理员<br/>ADMIN]
    SYSTEM[药品在线招投标系统]
    
    BIDDER -->|用户信息<br/>登录请求| SYSTEM
    SYSTEM -->|登录结果<br/>系统界面| BIDDER
    
    TENDERER -->|用户信息<br/>登录请求| SYSTEM
    SYSTEM -->|登录结果<br/>系统界面| TENDERER
    
    ADMIN -->|管理员信息<br/>登录请求| SYSTEM
    SYSTEM -->|登录结果<br/>系统界面| ADMIN
    
    BIDDER -->|投标信息| SYSTEM
    SYSTEM -->|招标信息<br/>投标结果| BIDDER
    
    TENDERER -->|招标信息| SYSTEM
    SYSTEM -->|招标列表<br/>投标列表| TENDERER
    
    ADMIN -->|管理操作| SYSTEM
    SYSTEM -->|用户列表<br/>系统数据| ADMIN
    
    style SYSTEM fill:#2563eb,color:#fff
    style BIDDER fill:#e1ffe1
    style TENDERER fill:#ffe1e1
    style ADMIN fill:#e1e1ff
            </div>
        </div>
        
        <!-- 1层：系统分解图 -->
        <div class="diagram-container">
            <div class="diagram-title">1层：系统分解图（Level 1 DFD）</div>
            <div class="mermaid" id="dfd-level1">
flowchart TB
    subgraph External["外部实体"]
        BIDDER[投标单位]
        TENDERER[招标单位]
        ADMIN[系统管理员]
    end
    
    subgraph System["系统处理过程"]
        P1[P1: 用户认证<br/>登录/注册]
        P2[P2: 招标管理<br/>发布/编辑/浏览]
        P3[P3: 投标管理<br/>提交/查看投标]
        P4[P4: 留言管理<br/>发布/查看/删除]
        P5[P5: 用户管理<br/>查看/修改角色]
    end
    
    subgraph Storage["数据存储"]
        D1[("D1: 用户数据<br/>USERS")]
        D2[("D2: 招标数据<br/>TENDERS")]
        D3[("D3: 投标数据<br/>BIDS")]
        D4[("D4: 留言数据<br/>MESSAGES")]
    end
    
    %% 用户认证流程
    BIDDER -->|登录信息| P1
    TENDERER -->|登录信息| P1
    ADMIN -->|登录信息| P1
    P1 -->|用户信息| D1
    D1 -->|验证结果| P1
    P1 -->|登录结果| BIDDER
    P1 -->|登录结果| TENDERER
    P1 -->|登录结果| ADMIN
    
    BIDDER -->|注册信息| P1
    TENDERER -->|注册信息| P1
    P1 -->|新用户数据| D1
    
    %% 招标管理流程
    TENDERER -->|招标信息| P2
    P2 -->|招标数据| D2
    D2 -->|招标列表| P2
    P2 -->|招标列表| BIDDER
    P2 -->|招标列表| TENDERER
    P2 -->|招标列表| ADMIN
    P2 -->|招标详情| BIDDER
    P2 -->|招标详情| TENDERER
    P2 -->|招标详情| ADMIN
    
    D1 -->|创建者信息| P2
    P2 -->|更新数据| D2
    
    %% 投标管理流程
    BIDDER -->|投标信息| P3
    P3 -->|投标数据| D3
    D3 -->|投标列表| P3
    P3 -->|我的投标| BIDDER
    P3 -->|项目投标列表| TENDERER
    P3 -->|所有投标| ADMIN
    
    D1 -->|投标者信息| P3
    D2 -->|招标项目信息| P3
    
    %% 留言管理流程
    BIDDER -->|留言内容| P4
    TENDERER -->|留言内容| P4
    ADMIN -->|留言内容| P4
    P4 -->|留言数据| D4
    D4 -->|留言列表| P4
    P4 -->|留言列表| BIDDER
    P4 -->|留言列表| TENDERER
    P4 -->|留言列表| ADMIN
    
    D1 -->|作者信息| P4
    D2 -->|关联项目信息| P4
    ADMIN -->|删除指令| P4
    P4 -->|删除操作| D4
    
    %% 用户管理流程
    ADMIN -->|管理请求| P5
    P5 -->|用户列表| D1
    D1 -->|用户数据| P5
    P5 -->|用户列表| ADMIN
    P5 -->|更新角色| D1
    
    style P1 fill:#ffe1f5
    style P2 fill:#ffe1e1
    style P3 fill:#e1ffe1
    style P4 fill:#fff5e1
    style P5 fill:#e1e1ff
    style D1 fill:#f1f5f9,stroke:#2563eb,stroke-width:3px
    style D2 fill:#f1f5f9,stroke:#2563eb,stroke-width:3px
    style D3 fill:#f1f5f9,stroke:#2563eb,stroke-width:3px
    style D4 fill:#f1f5f9,stroke:#2563eb,stroke-width:3px
    style BIDDER fill:#e1ffe1
    style TENDERER fill:#ffe1e1
    style ADMIN fill:#e1e1ff
            </div>
        </div>
        
        <div class="instructions">
            <h2>数据流图说明</h2>
            
            <h3>符号说明：</h3>
            <ul>
                <li><strong>矩形框</strong>：外部实体（External Entity），表示系统的数据源或目的地</li>
                <li><strong>圆角矩形</strong>：处理过程（Process），表示对数据的处理或转换</li>
                <li><strong>双圆</strong>：数据存储（Data Store），表示数据的存储位置</li>
                <li><strong>箭头</strong>：数据流（Data Flow），表示数据的流动方向和内容</li>
            </ul>
            
            <h3>0层：上下文图说明</h3>
            <p>上下文图显示了系统与外部实体之间的主要数据交换：</p>
            <ul>
                <li><strong>投标单位</strong>：提交投标信息，查看招标信息和投标结果</li>
                <li><strong>招标单位</strong>：发布招标信息，查看招标列表和投标列表</li>
                <li><strong>系统管理员</strong>：执行管理操作，查看系统数据</li>
            </ul>
            
            <h3>1层：系统分解图说明</h3>
            <table>
                <thead>
                    <tr>
                        <th>处理过程</th>
                        <th>功能说明</th>
                        <th>涉及数据存储</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>P1: 用户认证</td>
                        <td>处理用户登录、注册和身份验证</td>
                        <td>D1: 用户数据</td>
                    </tr>
                    <tr>
                        <td>P2: 招标管理</td>
                        <td>处理招标信息的发布、编辑、浏览和查询</td>
                        <td>D1: 用户数据, D2: 招标数据</td>
                    </tr>
                    <tr>
                        <td>P3: 投标管理</td>
                        <td>处理投标信息的提交、查询和审核</td>
                        <td>D1: 用户数据, D2: 招标数据, D3: 投标数据</td>
                    </tr>
                    <tr>
                        <td>P4: 留言管理</td>
                        <td>处理留言的发布、查看和删除</td>
                        <td>D1: 用户数据, D2: 招标数据, D4: 留言数据</td>
                    </tr>
                    <tr>
                        <td>P5: 用户管理</td>
                        <td>处理用户信息的查看和角色修改</td>
                        <td>D1: 用户数据</td>
                    </tr>
                </tbody>
            </table>
            
            <h3>数据存储说明：</h3>
            <ul>
                <li><strong>D1: 用户数据（USERS）</strong>：存储用户基本信息、角色和认证信息</li>
                <li><strong>D2: 招标数据（TENDERS）</strong>：存储招标项目的详细信息</li>
                <li><strong>D3: 投标数据（BIDS）</strong>：存储投标单位提交的投标信息</li>
                <li><strong>D4: 留言数据（MESSAGES）</strong>：存储用户发布的留言内容</li>
            </ul>
            
            <h3>主要数据流：</h3>
            <ul>
                <li><strong>用户信息流</strong>：用户 ↔ 用户认证 ↔ 用户数据</li>
                <li><strong>招标信息流</strong>：招标单位 ↔ 招标管理 ↔ 招标数据</li>
                <li><strong>投标信息流</strong>：投标单位 ↔ 投标管理 ↔ 投标数据</li>
                <li><strong>留言信息流</strong>：用户 ↔ 留言管理 ↔ 留言数据</li>
                <li><strong>管理信息流</strong>：管理员 ↔ 用户管理 ↔ 用户数据</li>
            </ul>
            
            <h3>导出图片方法：</h3>
            <ol>
                <li><strong>SVG导出（推荐）：</strong>点击"📥 导出为SVG图片"按钮</li>
                <li><strong>备用PNG导出：</strong>点击"📥 备用：导出PNG"按钮</li>
                <li><strong>PNG导出：</strong>点击"📥 导出为PNG图片"按钮</li>
                <li><strong>手动保存：</strong>右键点击数据流图，选择"图片另存为"</li>
            </ol>
        </div>
    </div>
    <script>
        let mermaidReady = false;
        let html2canvasReady = false;
        const statusMessage = document.getElementById('statusMessage');

        function showStatus(message, type = 'info') {
            statusMessage.textContent = message;
            statusMessage.className = 'status-message ' + type;
            statusMessage.style.display = 'block';
            setTimeout(() => {
                statusMessage.style.display = 'none';
            }, 5000);
        }

        function checkLibraries() {
            html2canvasReady = typeof html2canvas !== 'undefined';
            if (!html2canvasReady) {
                console.warn('html2canvas未加载，将使用SVG导出作为备用方案');
            }
        }

        function waitForMermaid(callback) {
            let loadedCount = 0;
            const totalDiagrams = 2;
            
            const checkInterval = setInterval(() => {
                const svgs = document.querySelectorAll('.mermaid svg');
                if (svgs.length >= totalDiagrams) {
                    let allLoaded = true;
                    svgs.forEach(svg => {
                        if (svg.children.length === 0) {
                            allLoaded = false;
                        }
                    });
                    if (allLoaded) {
                        clearInterval(checkInterval);
                        mermaidReady = true;
                        if (callback) callback();
                    }
                }
            }, 100);

            setTimeout(() => {
                clearInterval(checkInterval);
                if (!mermaidReady) {
                    showStatus('数据流图加载超时，请刷新页面重试', 'error');
                }
            }, 15000);
        }

        mermaid.initialize({ 
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis',
                padding: 20
            }
        });

        async function exportAsPNG() {
            const btn = document.getElementById('exportPngBtn');
            btn.disabled = true;
            btn.textContent = '⏳ 正在导出...';

            try {
                checkLibraries();
                if (!html2canvasReady) {
                    showStatus('html2canvas库未加载，请使用SVG导出或备用方案', 'error');
                    btn.disabled = false;
                    btn.textContent = '📥 导出为PNG图片';
                    return;
                }

                if (!mermaidReady) {
                    showStatus('等待数据流图加载完成...', 'info');
                    await new Promise(resolve => waitForMermaid(resolve));
                    await new Promise(resolve => setTimeout(resolve, 1000));
                }

                const containers = document.querySelectorAll('.diagram-container');
                if (containers.length === 0) {
                    throw new Error('图表容器未找到');
                }

                showStatus('正在生成图片，请稍候...', 'info');

                // 导出所有图表容器
                for (let i = 0; i < containers.length; i++) {
                    const container = containers[i];
                    const canvas = await html2canvas(container, {
                        backgroundColor: '#ffffff',
                        scale: 2,
                        logging: false,
                        useCORS: true,
                        allowTaint: true,
                        width: container.scrollWidth,
                        height: container.scrollHeight
                    });
                    
                    const link = document.createElement('a');
                    link.download = `data_flow_diagram_level${i}.png`;
                    link.href = canvas.toDataURL('image/png');
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    
                    // 延迟以避免浏览器阻止多个下载
                    if (i < containers.length - 1) {
                        await new Promise(resolve => setTimeout(resolve, 500));
                    }
                }
                
                showStatus('PNG图片已成功下载！', 'success');
            } catch (error) {
                console.error('导出PNG失败:', error);
                showStatus('PNG导出失败: ' + error.message + '。请尝试使用SVG导出或备用方案。', 'error');
            } finally {
                btn.disabled = false;
                btn.textContent = '📥 导出为PNG图片';
            }
        }

        async function exportAsPNGManual() {
            try {
                const svgs = document.querySelectorAll('.mermaid svg');
                if (svgs.length === 0) {
                    showStatus('SVG元素未找到，请等待数据流图加载完成', 'error');
                    return;
                }

                showStatus('正在通过SVG生成PNG，请稍候...', 'info');

                for (let i = 0; i < svgs.length; i++) {
                    const svg = svgs[i];
                    const svgData = new XMLSerializer().serializeToString(svg);
                    const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
                    const svgUrl = URL.createObjectURL(svgBlob);

                    const img = new Image();
                    img.crossOrigin = 'anonymous';
                    
                    await new Promise((resolve, reject) => {
                        img.onload = resolve;
                        img.onerror = reject;
                        img.src = svgUrl;
                    });

                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    canvas.width = img.width * 2;
                    canvas.height = img.height * 2;
                    ctx.fillStyle = '#ffffff';
                    ctx.fillRect(0, 0, canvas.width, canvas.height);
                    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                    const link = document.createElement('a');
                    link.download = `data_flow_diagram_level${i}.png`;
                    link.href = canvas.toDataURL('image/png');
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);

                    URL.revokeObjectURL(svgUrl);
                    
                    if (i < svgs.length - 1) {
                        await new Promise(resolve => setTimeout(resolve, 500));
                    }
                }

                showStatus('PNG图片已成功下载（通过SVG转换）！', 'success');
            } catch (error) {
                console.error('备用PNG导出失败:', error);
                showStatus('导出失败: ' + error.message, 'error');
            }
        }

        async function exportAsSVG() {
            const btn = document.getElementById('exportSvgBtn');
            btn.disabled = true;
            btn.textContent = '⏳ 正在导出...';

            try {
                if (!mermaidReady) {
                    showStatus('等待数据流图加载完成...', 'info');
                    await new Promise(resolve => waitForMermaid(resolve));
                    await new Promise(resolve => setTimeout(resolve, 1000));
                }

                const svgs = document.querySelectorAll('.mermaid svg');
                if (svgs.length === 0) {
                    throw new Error('SVG元素未找到，请等待数据流图加载完成');
                }

                showStatus('正在生成SVG图片...', 'info');

                for (let i = 0; i < svgs.length; i++) {
                    const svg = svgs[i];
                    const svgData = new XMLSerializer().serializeToString(svg);
                    const svgWithStyle = '<?xml version="1.0" encoding="UTF-8"?>\n' + 
                        '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
                        svgData;
                    
                    const svgBlob = new Blob([svgWithStyle], { type: 'image/svg+xml;charset=utf-8' });
                    const svgUrl = URL.createObjectURL(svgBlob);
                    
                    const link = document.createElement('a');
                    link.download = `data_flow_diagram_level${i}.svg`;
                    link.href = svgUrl;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    
                    URL.revokeObjectURL(svgUrl);
                    
                    if (i < svgs.length - 1) {
                        await new Promise(resolve => setTimeout(resolve, 500));
                    }
                }
                
                showStatus('SVG图片已成功下载！', 'success');
            } catch (error) {
                console.error('导出SVG失败:', error);
                showStatus('SVG导出失败: ' + error.message, 'error');
            } finally {
                btn.disabled = false;
                btn.textContent = '📥 导出为SVG图片';
            }
        }

        window.addEventListener('load', function() {
            checkLibraries();
            waitForMermaid(() => {
                showStatus('数据流图加载完成，可以开始导出了！', 'success');
            });
        });

        setTimeout(() => {
            checkLibraries();
            if (typeof html2canvas === 'undefined') {
                showStatus('提示：html2canvas库加载失败，建议使用SVG导出或备用PNG导出', 'info');
            }
        }, 2000);
    </script>
</body>
</html>

